/*
 * Copyright (c) 2011 Salzburg Research.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package at.newmedialab.skwrl.model;

import at.newmedialab.skwrl.api.SKWRLBackend;

import java.util.Iterator;
import java.util.List;

/**
 * This class is used to represent a reasoning rule of the form
 *
 * BODY -> HEAD
 *
 * where BODY is a list of graph patterns with variables and
 * HEAD is a single graph pattern used for constructing new triples.
 * <p/>
 * Rules are always in disjunctive normal form, i.e. the BODY contains
 * only conjunctions of patterns and no disjunctions, and the HEAD
 * consists of only a single pattern.
 * <p/>
 * A graph pattern is of the form
 *
 * (S,P,O) or (S,P,O,C)
 *
 * where S is the subject, P the predicate, O the object, and C the context
 * of a triple. S, P, and O may be either URIs, abbreviated local names or
 * variables. C may only be a URI of abbreviated local name.
 * <p/>
 * Rules are "safe", i.e. variables occurring in the HEAD also have to
 * appear at least once in the body.
 *
 * User: sschaffe
 */
public class Rule<Node> implements SKWRLConstruct<Node>{

    private Long id;


    /**
     * The name of this rule. Mainly used for displaying it to the user.
     */
    private String name;

    /**
     * The human-readable description of this rule. Mainly used for displaying it to the user.
     */
    private String description;

    /**
     * The head of this rule as a pattern. Note that all variables occurring in the
     * head also have to occur at least once in the body of the rule.
     */
    private Pattern<Node> head;


    /**
     * The body of this rule as a list of patterns. Note that the order of patterns
     * is not guaranteed to be the same as specified when creating the rule. The reasoner
     * will execute patterns in the order it thinks is most appropriate.
     */
    private List<Pattern<Node>> body;


    public Rule() {
    }



    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Pattern<Node> getHead() {
        return head;
    }

    public void setHead(Pattern<Node> head) {
        this.head = head;
    }

    public List<Pattern<Node>> getBody() {
        return body;
    }

    public void setBody(List<Pattern<Node>> body) {
        this.body = body;
    }


    public String toSKWRLString(SKWRLBackend<?,Node> backend) {
        String s = "";

        for(Iterator<Pattern<Node>> it = getBody().iterator(); it.hasNext(); ) {
            s += it.next().toSKWRLString(backend);

            if(it.hasNext()) {
                s += ", ";
            } else {
                s += " ";
            }
        }

        s += "-> ";

        s += getHead().toSKWRLString(backend);

        return s;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        if(id == null && name == null) {
            return super.equals(o);
        }

        Rule rule = (Rule) o;

        if (id != null ? !id.equals(rule.id) : rule.id != null) return false;
        if (!name.equals(rule.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = id != null ? id.hashCode() : 0;
        result = 31 * result + name.hashCode();

        if(result == 0) {
            return super.hashCode();
        } else {
            return result;
        }
    }
}
